/*
Copyright SecureKey Technologies Inc. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package status

import (
	"fmt"

	"github.com/pkg/errors"

	pb "git.cloud.inspur.com/ichain/ichain-protos-go/inode"
	"git.cloud.inspur.com/ichain/ichain-sdk-go/pkg/common/errors/multi"
	grpcstatus "google.golang.org/grpc/status"
)

type Status struct {
	// Group status group
	Group Group
	// Code status code
	Code int32
	// Message status message
	Message string
	// Details any additional status details
	Details []interface{}
}

type Group int32

const (
	// UnknownStatus unknown status group
	UnknownStatus Group = iota

	// ---------传输层状态码---------

	GRPCTransportStatus
	HTTPTransportStatus

	// ---------服务端状态码---------

	// EndorserServerStatus 背书节点状态码
	EndorserServerStatus
	// EventServerStatus 事件服务状态码
	EventServerStatus
	// ConsenserServerStatus 共识节点服务状态码
	ConsenserServerStatus
	CAServerStatus

	// ---------客户端相关状态码---------

	// EndorserClientStatus 背书客户端状态码
	EndorserClientStatus
	// ConsenserClientStatus 共识节点客户端状态码
	ConsenserClientStatus
	// ClientStatus 客户端状态码
	ClientStatus

	// ContractStatus 合约返回的状态码
	ContractStatus

	// DiscoveryServerStatus  服务发现的返回状态码
	DiscoveryServerStatus

	// ---------其他---------

	TestStatus
)

var GroupName = map[int32]string{
	0:  "Unknown",
	1:  "gRPC Transport Status",
	2:  "HTTP Transport Status",
	3:  "Endorser Server Status",
	4:  "Event Server Status",
	5:  "Consenser Server Status",
	6:  "CA Server Status",
	7:  "Endorser Client Status",
	8:  "Consenser Client Status",
	9:  "Client Status",
	10: "Contract status",
	11: "Discovery status",
	12: "Test status",
}

func (g Group) String() string {
	if s, ok := GroupName[int32(g)]; ok {
		return s
	}
	return UnknownStatus.String()
}

// FromError 在error中提取Status
// @param err
// @return s
// @return ok
func FromError(err error) (s *Status, ok bool) {
	if err == nil {
		return &Status{Code: int32(OK)}, true
	}
	if s, ok := err.(*Status); ok {
		return s, true
	}
	unwrappedErr := errors.Cause(err)
	if s, ok := unwrappedErr.(*Status); ok {
		return s, true
	}
	if m, ok := unwrappedErr.(multi.Errors); ok {
		var errs []interface{}
		for _, err := range m {
			errs = append(errs, err)
		}
		return New(ClientStatus, MultipleErrors.ToInt32(), m.Error(), errs), true
	}

	return nil, false
}

func (s *Status) Error() string {
	return fmt.Sprintf("%s Code: (%d) %s. Description: %s", s.Group.String(), s.Code, s.codeString(), s.Message)
}

func (s *Status) codeString() string {
	switch s.Group {
	case GRPCTransportStatus:
		return ToGRPCStatusCode(s.Code).String()
	case EndorserServerStatus, ConsenserServerStatus:
		return ToCommonStatusCode(s.Code).String()
	case EventServerStatus:
		return ToTransactionValidationCode(s.Code).String()
	case EndorserClientStatus, ConsenserClientStatus, ClientStatus:
		return ToSDKStatusCode(s.Code).String()
	default:
		return Unknown.String()
	}
}

func New(group Group, code int32, msg string, details []interface{}) *Status {
	return &Status{Group: group, Code: code, Message: msg, Details: details}
}

func NewFromProposalResponse(res *pb.ProposalResponse, endorser string) *Status {
	if res == nil {
		return nil
	}
	details := []interface{}{endorser, res.Response.Payload}

	return New(EndorserServerStatus, res.Response.Status, res.Response.Message, details)
}

func NewFromGRPCStatus(s *grpcstatus.Status) *Status {
	if s == nil {
		return nil
	}
	details := make([]interface{}, len(s.Proto().Details))
	for i, detail := range s.Proto().Details {
		details[i] = detail
	}

	return &Status{Group: GRPCTransportStatus, Code: s.Proto().Code,
		Message: s.Message(), Details: details}
}

func NewFromExtractedContractError(code int, message string) *Status {
	return &Status{Group: ContractStatus, Code: int32(code),
		Message: message, Details: nil}
}
